﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace DevelopersTestPad.Worksheets.DotNet
{
    /// <summary>
    /// A panel with a caption and an image.
    /// </summary>
    internal partial class HeaderPanel : Panel
    {
        public event EventHandler<HeaderPanelActionPermissionRequest> ActionPermissionRequest;
        public event EventHandler Maximize;
        public event EventHandler Restore;
        public event EventHandler Minimize;

        /// <summary>
        /// The header caption.
        /// </summary>
        public string Caption { get; set; }

        /// <summary>
        /// Is the cursor inside the context menu arraw background?
        /// </summary>
        private bool _isCursorInsideContextMenuArrowBacgroundBounds;

        /// <summary>
        /// Is the context menu currently opened?
        /// </summary>
        private bool _isContextMenuOpened;

        public HeaderPanel()
        {
            InitializeComponent();
            InitializeEx();
        }

        private void InitializeEx()
        {
            MouseMove += HeaderPanel_MouseMove;

            mniMaximizeSize.Click += mniMaximizeSize_Click;
            mniRestoreSize.Click += mniRestoreSize_Click;
        }


        protected override void OnPaintBackground(PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            // Fill the background.
            Rectangle rect = new Rectangle(0, 0, Width, 20);
            LinearGradientBrush brush = new LinearGradientBrush(rect,
                                                                Color.FromArgb(0, 122, 204),
                                                                Color.FromArgb(255, 79, 146, 219),
                                                                LinearGradientMode.ForwardDiagonal);

            g.FillRectangle(brush, rect);

            // Draw the header caption.
            Font font = new Font("Segoe UI", 9, FontStyle.Regular);
            if (Caption != null)
            {
                g.DrawString(Caption, font, Brushes.White, 3, 1.8f);
            }


            // Draw the context menu arrow image and background

            const string arrowDown = "▼";
            Font arrowDownFont = new Font("Segoe UI", 6, FontStyle.Regular);
            
            Rectangle arrowDownBounds = GetContextMenuImageBounds();

            if (_isCursorInsideContextMenuArrowBacgroundBounds)
            {
                g.FillRectangle(new SolidBrush(Color.FromArgb(255, 231, 232, 236)), GetContextMenuArrowBackgroundBounds());
                g.DrawString(arrowDown, arrowDownFont, Brushes.Teal, arrowDownBounds.X, arrowDownBounds.Y);
            }
            else
            {
                g.DrawString(arrowDown, arrowDownFont, Brushes.White, arrowDownBounds.X, arrowDownBounds.Y);                
            }
        }

        /// <summary>
        /// Gets the bounds of the context menu image.
        /// </summary>
        private Rectangle GetContextMenuImageBounds()
        {
            return new Rectangle(Width - 14, 4, 0, 0);
        }

        /// <summary>
        /// Gets the bounds of the context menu image background.
        /// </summary>
        private Rectangle GetContextMenuArrowBackgroundBounds()
        {
            Rectangle imageBounds = GetContextMenuImageBounds();
            return new Rectangle(imageBounds.X - 3, imageBounds.Y - 2, 15, 15);
        }

        /// <summary>
        /// Show the context menu if the "arrow" was clicked.
        /// </summary>
        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Rectangle bounds = GetContextMenuArrowBackgroundBounds();

                bool inside = bounds.Contains(e.X, e.Y);

                if (inside)
                {
                    contextMenuStrip.Show(this, new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height), ToolStripDropDownDirection.BelowLeft);
                    return;                    
                }
            }

            base.OnMouseClick(e);
        }

        private int _previousViewSizeIteration = ChangeViewSizeIterationRestore;
        private int _currentViewSizeIteration = ChangeViewSizeIterationRestore;

        private const int ChangeViewSizeIterationMaximize = 1;
        private const int ChangeViewSizeIterationRestore = 2;
        private const int ChangeViewSizeIterationMinimize = 3;

        private void MoveToNextViewSizeIteration()
        {
            int nextIteration = -1;

            switch (_currentViewSizeIteration)
            {
                case ChangeViewSizeIterationMaximize:
                    nextIteration = ChangeViewSizeIterationRestore;
                    break;

                case ChangeViewSizeIterationRestore:
                    switch (_previousViewSizeIteration)
                    {
                        case ChangeViewSizeIterationMaximize:
                            nextIteration = ChangeViewSizeIterationMinimize;
                            break;
                        case ChangeViewSizeIterationRestore:
                            nextIteration = ChangeViewSizeIterationMaximize;
                            break;
                        case ChangeViewSizeIterationMinimize:
                            nextIteration = ChangeViewSizeIterationMaximize;
                            break;
                    }
                    break;

                case ChangeViewSizeIterationMinimize:
                    nextIteration = ChangeViewSizeIterationRestore;
                    break;
            }

            _previousViewSizeIteration = _currentViewSizeIteration;
            _currentViewSizeIteration = nextIteration;
        }

        /// <summary>
        /// Raise the Maximize, Restore or Minimize event...
        /// </summary>
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Rectangle bounds = GetContextMenuArrowBackgroundBounds();

                bool inside = bounds.Contains(e.X, e.Y);

                if (inside == false)
                {
                    MoveToNextViewSizeIteration();

                    switch (_currentViewSizeIteration)
                    {
                        case ChangeViewSizeIterationMaximize:
                            OnMaximize(EventArgs.Empty);
                            break;
                        case ChangeViewSizeIterationMinimize:
                            OnMinimize(EventArgs.Empty);
                            break;
                        case ChangeViewSizeIterationRestore:
                            OnRestore(EventArgs.Empty);
                            break;
                    }
                    return;                    
                }
            }

            base.OnMouseDoubleClick(e);
        }

        private void HeaderPanel_MouseMove(object sender, MouseEventArgs e)
        {
            bool inside = GetContextMenuArrowBackgroundBounds().Contains(e.X, e.Y);

            bool previous = _isCursorInsideContextMenuArrowBacgroundBounds;
            
            _isCursorInsideContextMenuArrowBacgroundBounds = inside;
            
            if (inside != previous)
            {                        
                Invalidate();
            }            
        }

        protected override void OnMouseLeave(System.EventArgs e)
        {
            if (_isContextMenuOpened == false)
            {
                _isCursorInsideContextMenuArrowBacgroundBounds = false;
                Invalidate();
            }

            base.OnMouseLeave(e);
        }

        private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            HeaderPanelActionPermissionRequest permissionRequest = new HeaderPanelActionPermissionRequest();
            OnActionPermissionRequest(permissionRequest);

            mniMaximizeSize.Enabled = permissionRequest.IsMaximizeAvailable;
            mniRestoreSize.Enabled = permissionRequest.IsRestoreAvailable;
            mniMinimize.Enabled = permissionRequest.IsMinimizeAvailable;

            _isContextMenuOpened = true;
        }

        private void contextMenuStrip_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            _isContextMenuOpened = false;
            _isCursorInsideContextMenuArrowBacgroundBounds = false;
            
            Invalidate();
        }

        private void mniMaximizeSize_Click(object sender, System.EventArgs e)
        {
            OnMaximize(EventArgs.Empty);
        }

        private void mniRestoreSize_Click(object sender, System.EventArgs e)
        {
            OnRestore(EventArgs.Empty);
        }

        private void mniMinimize_Click(object sender, EventArgs e)
        {
            OnMinimize(EventArgs.Empty);
        }

        protected void OnActionPermissionRequest(HeaderPanelActionPermissionRequest e)
        {
            EventHandler<HeaderPanelActionPermissionRequest> handler = ActionPermissionRequest;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected void OnMaximize(EventArgs e)
        {
            EventHandler handler = Maximize;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected void OnRestore(EventArgs e)
        {
            EventHandler handler = Restore;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected void OnMinimize(EventArgs e)
        {
            EventHandler handler = Minimize;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public sealed class HeaderPanelActionPermissionRequest : EventArgs
        {
            public bool IsMaximizeAvailable { set; get; }
            public bool IsRestoreAvailable { set; get; }
            public bool IsMinimizeAvailable { set; get; }
        }
    }
}
